package ch.ethz.inf.shop.model.simpleDatabase.operators;

import java.util.ArrayList;
import java.util.List;

import ch.ethz.inf.shop.model.simpleDatabase.Tuple;
import ch.ethz.inf.shop.model.simpleDatabase.TupleSchema;

/**
 * Projection in relational algebra. Returns tuples that contain on projected
 * columns. Therefore the new tuples conform to a new schema.
 */
public final class Project extends Operator {

	private final Operator op;

	private final TupleSchema newSchema;

	private final TupleProjector projector;

	private static final TupleProjector DEFAULT_PROJECTOR = new TupleProjector() {

		@Override
		public Tuple project(Tuple t, TupleSchema schema) {
			ArrayList<String> newVals = new ArrayList<String>(schema.getColumnNames().size());
			for (String col : schema.getColumnNames()) {
				newVals.add(t.getString(col));
			}
			return new Tuple(schema, newVals);
		}
	};

	/**
	 * Constructs a new projection operator.
	 * 
	 * @param op
	 *            operator to pull from
	 * @param column
	 *            single column name that will be projected
	 */
	public Project(final Operator op, final String column) {
		this.op = op;
		List<String> columns = new ArrayList<String>();
		columns.add(column);
		this.newSchema = new TupleSchema(columns);

		projector = DEFAULT_PROJECTOR;
	}

	/**
	 * Constructs a new projection operator.
	 * 
	 * @param op
	 *            operator to pull from
	 * @param columns
	 *            column names that will be projected
	 */
	public Project(final Operator op, final String[] columnsPar) {
		this.op = op;
		List<String> columns = new ArrayList<String>();
		for (String string : columnsPar) {
			columns.add(string);
		}
		this.newSchema = new TupleSchema(columns);

		projector = DEFAULT_PROJECTOR;
	}

	public Project(final Operator op, final TupleProjector projector) {
		this.op = op;
		this.projector = projector;
		this.newSchema = null;
	}

	/**
	 * Constructs a new projection operator.
	 * 
	 * @param op
	 *            operator to pull from
	 * @param columns
	 *            column names that will be projected
	 */
	public Project(final Operator op, final List<String> columns) {
		this.op = op;

		this.newSchema = new TupleSchema(columns);

		projector = DEFAULT_PROJECTOR;
	}

	@Override
	public boolean moveNext() {

		// get next tuple from child operator
		if (!this.op.moveNext()) {
			return false;
		}
		final Tuple t = this.op.current();

		current = projector.project(t, this.newSchema);
		return true;
	}
}
